using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Threading.Tasks;
using GrapeCity.Forguncy.Commands;
using GrapeCity.Forguncy.Plugin;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace PdfOperationCommand
{
    [Icon("pack://application:,,,/PdfOperationCommand;component/Resources/Split.png")]
    [OrderWeight(6)]
    [Description("分割单页PDF文件，使用iTextSharp实现")]
    public class PdfDivideServerCommand : BaseServerCommand, ICommandExecutableInServerSideAsync
    {
        [Required]
        [FormulaProperty]
        [DisplayName("文件路径")]
        [Description("文件绝对路径，如：D:\\example.pdf(Windows)，/path/example.pdf(Linux)")]
        public object FilePath { get; set; }

        [DisplayName("分割PDF文件页码")]
        [Description("PDF文件页码（默认为 1，即第一页）")]
        [IntProperty(Min = 1)]
        [FormulaProperty]
        public Object PageIndex { get; set; } = 1;

        [DisplayName("横向分割")]
        [BoolProperty(IndentLevel = 0)]
        public bool RowDividing { get; set; }

        [DisplayName("横向均匀分割线")]
        [Description("添加若干条横向分割线，均匀分割（比如添加一条分割线，会横向均分为两页）")]
        [IntProperty(Min = 1)]
        [FormulaProperty]
        public Object RowUniformLineCount { get; set; } = 1;

        [DisplayName("距离分割线（cm）")]
        [Description("间隔指定距离（cm），添加一条横向分割线（这一项设置了则按距离分割，否则进行均匀分割）")]
        [DoubleProperty(Min = 0)]
        [FormulaProperty]
        public Object RowDistanceLine { get; set; } = 0;

        [DisplayName("纵向分割")]
        [BoolProperty(IndentLevel = 0)]
        public bool ColDividing { get; set; }

        [DisplayName("纵向均匀分割线")]
        [Description("添加若干条纵向分割线，均匀分割（比如添加一条分割线，会纵向均分为两页）")]
        [IntProperty(Min = 1)]
        [FormulaProperty]
        public Object ColUniformLineCount { get; set; } = 1;

        [DisplayName("距离分割线（cm）")]
        [Description("间隔指定距离（cm），添加一条纵向分割线（这一项设置了则按距离分割，否则进行均匀分割）")]
        [DoubleProperty(Min = 0)]
        [FormulaProperty]
        public Object ColDistanceLine { get; set; } = 0;

        [Required]
        [FormulaProperty]
        [DisplayName("输出文件路径")]
        [Description("文件绝对路径，如：D:\\example.pdf(Windows)，/path/example.pdf(Linux)")]
        public object OutFilePath { get; set; }

        public async Task<ExecuteResult> ExecuteAsync(IServerCommandExecuteContext dataContext)
        {
            var inputFilePath = await GetParamValue(dataContext, FilePath);
            var outputFilePath = await GetParamValue(dataContext, OutFilePath);
            var rowLineCount = 0;
            var colLineCount = 0;
            double rowDistance = 0;
            double colDistance = 0;
            if (RowDividing)
            {
                rowLineCount = int.Parse((await GetParamValue(dataContext, RowUniformLineCount)).ToString());
                rowDistance = double.Parse((await GetParamValue(dataContext, RowDistanceLine)).ToString());
            }

            if (ColDividing)
            {
                colLineCount = int.Parse((await GetParamValue(dataContext, ColUniformLineCount)).ToString());
                colDistance = double.Parse((await GetParamValue(dataContext, ColDistanceLine)).ToString());
            }

            var pageIndex = int.Parse((await GetParamValue(dataContext, PageIndex)).ToString());

            DividePdf(inputFilePath?.ToString(), outputFilePath?.ToString(), rowLineCount, colLineCount,
                rowDistance, colDistance, pageIndex);
            return new ExecuteResult();
        }

        private static void DividePdf(string inputFilePath, string outputFilePath, int rowLineCount,
            int colLineCount, double rowDistance, double colDistance, int pageIndex)
        {
            PdfReader reader = null;
            FileStream fs = null;
            Document document = null;
            PdfWriter writer = null;
            try
            {
                reader = new PdfReader(inputFilePath);
                fs = new FileStream(outputFilePath, FileMode.Create, FileAccess.Write);

                var pageSize = reader.GetPageSizeWithRotation(pageIndex);
                var pageWidth = pageSize.Width;
                var pageHeight = pageSize.Height;
                var height = pageHeight / (rowLineCount + 1);
                var width = pageWidth / (colLineCount + 1);

                if (rowDistance != 0)
                {
                    var pageHeightCm = ConvertPointsToCm(pageHeight);
                    rowLineCount = rowDistance > pageHeightCm ? 0 :1;
                    rowDistance = pageHeightCm - rowDistance;
                    height = (float) ConvertCmToPoints(rowDistance);
                }

                if (colDistance != 0)
                {
                    var pageWidthCm = ConvertPointsToCm(pageWidth);
                    colLineCount = colDistance > pageWidthCm ? 0 : 1;
                    width = (float) ConvertCmToPoints(colDistance);
                }

                document = new Document();
                writer = PdfWriter.GetInstance(document, fs);
                document.Open();

                for (var i = 0; i <= rowLineCount; i++)
                {
                    for (var j = 0; j <= colLineCount; j++)
                    {
                        var x = j * width;
                        var y = (rowLineCount - i) * height;

                        var newHeight = height;
                        var newWidth = width;
                        if (i == 0)
                        {
                            newHeight = pageHeight - rowLineCount * height;
                        }
                        if (j == colLineCount)
                        {
                            newWidth = pageWidth - colLineCount * width;
                        }
                        AddDividePage(document, writer, reader, pageIndex, x, y, newWidth, newHeight);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.Write(ex.Message);
                throw;
            }
            finally
            {
                document?.Close();
                writer?.Close();
                fs?.Close();
                reader?.Close();
            }
        }


        private static void AddDividePage(Document document, PdfWriter writer, PdfReader reader, int pageNumber,
            float x, float y, float width, float height)
        {
            document.SetPageSize(new Rectangle(width, height));
            document.NewPage();
            var cb = writer.DirectContent;
            var page = writer.GetImportedPage(reader, pageNumber);

            var template = cb.CreateTemplate(width, height);
            template.AddTemplate(page, -x, -y);
            cb.AddTemplate(template, 0, 0);
        }

        private static double ConvertPointsToCm(double points)
        {
            return (int)Math.Round(points * 2.54f / 72);
        }
        
        private static double ConvertCmToPoints(double cm)
        {
            return cm * (72 / 2.54);
        }

        public override bool GetDesignerPropertyVisible(string propertyName, CommandScope commandScope)
        {
            if (propertyName == nameof(RowUniformLineCount) || propertyName == nameof(RowDistanceLine))
            {
                return RowDividing;
            }

            if (propertyName == nameof(ColUniformLineCount) || propertyName == nameof(ColDistanceLine))
            {
                return ColDividing;
            }

            return base.GetDesignerPropertyVisible(propertyName, commandScope);
        }

        public override string ToString()
        {
            return "分割单页PDF文件";
        }
    }
}